import {
  MiddlewareConsumer,
  Module,
  NestModule,
  OnApplicationBootstrap,
  OnModuleInit,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

import {
  InjectEntityManager,
  InjectMikroORM,
  MikroOrmModule,
  logger,
} from '@mikro-orm/nestjs';
import { SqlHighlighter } from '@mikro-orm/sql-highlighter';
import { JsonApiResponse } from '@utils/json.api.response';

import { CacheModule } from '@nestjs/cache-manager';

import { ConfigModule, ConfigService } from '@nestjs/config';
import { redisStore } from 'cache-manager-redis-store';

//路由注册应该在Redis之后
import { registerRouter } from '@utils/api.router';

import { PigeonModule, PigeonService } from './modules/pigeon';
import { Transport } from './modules/pigeon/enum/pigeon.transport.enum';
import { DiscoveryModule, DiscoveryService } from '@golevelup/nestjs-discovery';
import { monotonicFactory, ulid } from 'ulid';

import 'knext';
import { BullModule } from '@nestjs/bull';
import { EntityManager, MikroORM } from '@mikro-orm/mysql';
import { Users } from './databases/entities/Users';

import { PublishPacket } from 'aedes';
import dayjs from 'dayjs';
import { randomInt } from 'crypto';
import { numberUnionId } from '@utils/nuid';
import { ServeStaticModule } from '@nestjs/serve-static';
import path, { join } from 'path';
import fs from 'fs'
import { HttpExceptionFilter } from './utils/http-exception.filter';
import { HttpModule } from '@nestjs/axios';
import { parse } from 'yaml';
import { MediaController } from './modules/workstation/common/api/media.controller';
import { SuperModule } from './modules/workstation/job/api/super/super.module';
import { WechatModule } from './modules/workstation/wechat/wechat.module';
import { WechatGatewayModule } from './modules/gateway/gateway.module';
import { auth } from 'qiniu';

const mqttPacket = require('mqtt-packet');

const ulids = monotonicFactory();
const aedesPersistenceRedis = require('aedes-persistence-redis');

// 备份代码: EntityGenerator
//const what = meta.className.match(/([A-Z][a-z]+)+?/g).pop().toLowerCase();

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // 设置为全局范围
      envFilePath: ['.env'], // 指定环境变量文件的路径
      load: [()=>parse(fs.readFileSync(path.resolve('config','default.yaml'),'utf8'))]
    }),

    //导入预处理的控制器模块
    ...registerRouter(),

    // CacheModule.register<RedisClientOptions>({
    //   store: redisStore,

    //   // Store-specific configuration:
    //   host: 'localhost',
    //   port: 6379,
    // }),

    CacheModule.registerAsync({
      isGlobal: true,
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        isGlobal: true,
        store: redisStore as any,
        socket: {
          host: '192.168.1.6', //configService.get('REDIS_HOST'),
          password:'b88888888',
          port: 6379, //configService.get('REDIS_PORT'),
        },
        ttl: 1000 * 1000, //configService.get('CACHE_TTL'),
        db: 0,
        //auth_pass:
      }),
    }),
    MikroOrmModule,
    MikroOrmModule.forRoot({
      contextName: 'typecho',
      registerRequestContext: false, 
      dbName: 'typecho',
      user: 'typecho',
      password: '88888888',
      type: 'mysql',
      host: '192.168.1.6',
      port: 3306,
      discovery: {
        warnWhenNoEntities: false,
      },
      charset: 'utf8mb4',
      //debug: true,
      //logger: logger.log.bind(logger),
      // highlighter: new SqlHighlighter(),
      // debug: true,
      // logger: logger.log.bind(logger),
      entities: ['./dist/databases/entities/typecho'],
      entitiesTs: ['./src/databases/entities/typecho']
    }),
    MikroOrmModule.forRoot({
      contextName: 'zp',
      registerRequestContext: false, 
      dbName: 'zp_apesk_net',
      user: 'zp_apesk_net',
      password: '88888888',
      type: 'mysql',
      host: '192.168.1.6',
      port: 3306,
      charset: 'utf8mb4',
      collate: 'utf8mb4',
      allowGlobalContext: true,
      highlighter: new SqlHighlighter(),
      debug: true,
      logger: logger.log.bind(logger),
      entities: ['./dist/entities', './dist/databases/entities'],
      entitiesTs: ['./src/entities', './src/databases/entities'],
    }),
    MikroOrmModule.forRoot({
      contextName: 'leadshop',
      registerRequestContext: false, 
      dbName: 'leadshop',
      user: 'leadshop',
      password: '88888888',
      type: 'mysql',
      host: '192.168.1.6',
      port: 3306,
      charset: 'utf8',
      collate: 'utf8_bin',
      allowGlobalContext: true,
      highlighter: new SqlHighlighter(),
      debug: true,
      logger: logger.log.bind(logger),
      entities: ['./dist/databases/entities/leadshop'],
      entitiesTs: ['./src/databases/entities/leadshop'],
    }),
    MikroOrmModule.forRoot({
      contextName: 'ims_yzd_kp',
      registerRequestContext: false, 
      dbName: 'zf_apesk_net',
      user: 'zf_apesk_net',
      password: '88888888',
      type: 'mysql',
      host: '192.168.1.6',
      port: 3306,
      charset: 'utf8',
      collate: 'utf8_bin',
      allowGlobalContext: true,
      highlighter: new SqlHighlighter(),
      debug: true,
      logger: logger.log.bind(logger),
      entities: ['./dist/databases/entities/ims_yzd_kp'],
      entitiesTs: ['./src/databases/entities/ims_yzd_kp'],
    }),
    PigeonModule.forRoot({
      port: 8083, // Port MQTT TCP Server
      transport: Transport.WS,
      id: 'binarybeast',
      concurrency: 100,
      queueLimit: 42,
      maxClientsIdLength: 23,
      connectTimeout: 30000,
      heartbeatInterval: 60000,
      persistence: aedesPersistenceRedis({
        port: 6379, // Redis port
        host: '192.168.1.6', // Redis host
        password: 'b88888888',
        family: 4, // 4 (IPv4) or 6 (IPv6)
        db: 1,
        maxSessionDelivery: 100, // maximum offline messages deliverable on client CONNECT, default is 1000
        packetTTL: function (packet) {
          // offline message TTL, default is disabled
          return 100; //seconds
        },
      }),
    }),
    BullModule.forRoot('mqtt', {
      redis: {
        host: '192.168.1.6',
        password: 'b88888888',
        port: 6379,
        db: 3,
      },
      prefix: 'mqueue',
    }),
    BullModule.registerQueue({
      configKey: 'mqtt',
      name: 'mqtt',
    }),
    // ServeStaticModule.forRoot({
    //   rootPath: join(__dirname, '..', 'static'),
    // }),
    AppModule,
    //PigeonModule,
    DiscoveryModule,
    SuperModule,
    //WechatModule,
    WechatGatewayModule
  ], 
  controllers: [AppController, MediaController],
  providers: [AppService, JsonApiResponse],
})
export class AppModule
  implements NestModule, OnModuleInit, OnApplicationBootstrap
{
  constructor(
    private readonly discover: DiscoveryService,
    private readonly pigeonService: PigeonService,
    @InjectMikroORM('zp')
    private readonly orm: MikroORM,
    @InjectEntityManager('zp')
    private readonly em: EntityManager,
  ) {}

  configure(consumer: MiddlewareConsumer) {
    //   for (let i = 0; i < 3; i++) {
    //      const id = numberUnionId()
    //      console.log(id)
    //   }
    //   const seed = dayjs().valueOf()
    //   for (let i = 0; i < 3; i++) {
    //     const id = ulids(seed) //ulid(seed)
    //     console.log(id)
    //  }
  }

  public async onModuleInit() {
    const providers =
      await this.discover.providersWithMetaAtKey<number>('metaKey');
  }

  public async onApplicationBootstrap() {
    // setTimeout(() => {
    //   // const packet = mqttPacket.generate({
    //   //   cmd: 'publish',
    //   //   retain: false,
    //   //   qos: 1,
    //   //   dup: false,
    //   //   length: 10,
    //   //   topic: 'chat2',
    //   //   payload: 'test' // Can also be a Buffer
    //   // })

    //   const packet : PublishPacket = {
    //     cmd: 'publish',
    //     qos:1,
    //     topic:'chat2',
    //     retain:false,
    //     dup:false,
    //     payload: 'HHHLLLO!SYS'
    //   }

    //   this.pigeonService.broker.publish(packet,(err)=>{
    //       console.log(err)
    //   })
    // }, 5000);

    // let broker = this.pigeonService.broker as any;
    // let persistence = broker.persistence

    // persistence.subscriptionsByClient({
    //   //id: client.id,
    //   //done,
    //  // arg
    // })

    //await this.addSubscriptions()
    //await this.pigeonService.subscribe('chat2')

    await this.allUserSubscript();
  }

  userToClientKey(id) {
    return 'chat-client:' + id;
  }

  async allUserSubscript() {
    const users = await this.em.find(
      Users,
      {},
      {
        fields: ['id', 'roomsAsChatRoomMembers'],
        populate: ['roomsAsChatRoomMembers'],
      },
    );

    for (const user of users) {
      const subs = user.roomsAsChatRoomMembers.map((room) => {
        return { topic: room.topic, qos: 1 };
      });

      //console.log(subs)
      //const uu = user
      //console.log(uu.roomsAsChatRoomMembers)

      await this.addUserSubscriptions(user.id, subs);
    }

    //await this.addSubscriptions(user.id)
    //console.log(users);
  }

  addUserSubscriptions(userId, subs) {
    return new Promise((resolve, reject) => {
      const broker = this.pigeonService.broker as any;
      const persistence = broker.persistence;

      persistence.addSubscriptions(
        { id: this.userToClientKey(userId) },
        subs,
        (err) => {
          resolve(null);
        },
      );
    });
  }

  addSubscriptions() {
    return new Promise((resolve, reject) => {
      const broker = this.pigeonService.broker as any;
      const persistence = broker.persistence;

      persistence.addSubscriptions(
        { id: 'client.id' },
        [{ topic: 'chat2', qos: 1 }],
        (err) => {
          resolve(null);
        },
      );
    });
  }
}
